/*
 * algorithm.c
 *
 *  Created on: 26/04/2013
 *      Author: jose
 */

#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "graph.h"
#include "algorithm.h"
#include "error.h"

/* TODO ver casos cuando la ventana es cero */
Window *
window_create(unsigned len)
{
	assert(len > 0);

	Window * w;

	w = (Window *) calloc(1, sizeof(Window));
	if (!w)
	{
		error_message(__FILE__, __FUNCTION__, __LINE__, "No se ha podido crear la ventana");
		return NULL;
	}

	/* Crear array que contendra los indices de los headnodes */
	w->window = (unsigned *) calloc(len, sizeof(unsigned));
	if (!(w->window))
	{
		error_message(__FILE__, __FUNCTION__, __LINE__, "No se ha podido crear la ventana");
		return NULL;
	}

	w->len = len;

	return w;
}

Sessions *
sessions_create()
{
	Sessions * s = NULL;

	s = (Sessions *) calloc(1, sizeof(Sessions));

	return s;
}

Session *
session_create(unsigned len)
{
	assert(len > 0);

	Session * s = NULL;

	s = (Session *) calloc(len, sizeof(Session));

	return s;
}


Access *
access_create(unsigned len)
{
	assert(len > 0);

	Access * s;

	s = (Access *) calloc(len, sizeof(Access));

	return s;
}

Graph_Adj_List *
sessions_load_urls_to_adj_list(Graph_Adj_List * gr, Sessions * sess)
{
	assert(gr);
	assert(sess);

	unsigned i, j;
	Session * s = NULL;

	/* Recorrer cada session */
	for (i=0 ; i < sess->len ; i++)
	{
		/* Recorrer cada access */
		for (j=0, s=&(sess->s[i]); j < s->len; j++)
		{
			/* Revisar si la URL ya existe cargada */
			if (!adj_list_search_headnode(gr, s->access[j].url))
				adj_list_update_next_empty_headnode(gr, s->access[j].url);
		}
	}
	return gr;

}

void
window_zero(Window * win)
{
	assert(win);

	memset(win->window, 0, win->len * sizeof(unsigned));

	win->empty = 0;
}


void
update_arcs_from_window(Graph_Adj_List * gr, Window * win, const String * url)
{
	assert(gr);
	assert(win);
	assert(url);

	unsigned i;
	Node * n_ptr = NULL;

	for (i=0 ; i < win->empty ; i++)
	{
		n_ptr = headnode_search_node(gr, win->window[i], url);
		if (!n_ptr)
			n_ptr = headnode_add_node(gr, gr->heads[win->window[i]].url, url);
	}

}

void
window_purge(Window * win)
{
	assert(win);

	unsigned i;

	/* Si la ventana está llena, hacer purge */
	if (win->empty >= win->len )
	{
		for (i=0 ; i<win->len-1 ; i++)
			win->window[i] = win->window[i+1];
		win->window[win->len-1] = 0;

		win->empty--;
	}
}

unsigned
window_add_to_window(Window * win, unsigned index, unsigned top)
{
	assert(win->empty < win->len);
	assert(index > 0);
	assert(index <= top);

	win->window[(win->empty)++] = index;

	return win->empty;
}

Graph_Adj_List *
calculate_conf(Graph_Adj_List * gr)
{
	assert(gr);

	unsigned i, end;
	Node * n_ptr = NULL;

	end = (gr->empty > gr->heads_len ? gr->heads_len : gr->empty);

	/* Recorrer todos los headnodes */
	for (i=1 ; i <= end ; i++)
	{
		/* Recorrer nodos de cada headnode */
		for (n_ptr = gr->heads[i].link ; n_ptr ; n_ptr = n_ptr->link)
		{
			n_ptr->conf = (float) n_ptr->arc / (float) gr->heads[i].appareances;
		}

	}

	return gr;
}

Results *
calculate_conf2(Graph_Adj_List * gr)
{
	assert(gr);

	Node * n_ptr = NULL;
	Results * res = NULL; // res apunta siempre al inicio de la lista
	Results * r_ptr = NULL, * r_temp = NULL;
	unsigned i, end;

	end = (gr->empty > gr->heads_len ? gr->heads_len : gr->empty);

	/* Recorrer todos los headnodes */
	for (i=1 ; i <= end ; i++)
	{
		/* Recorrer nodos de cada headnode */
		for (n_ptr = gr->heads[i].link ; n_ptr ; n_ptr = n_ptr->link)
		{
			n_ptr->conf = (float) n_ptr->arc / (float) gr->heads[i].appareances;

			/* Crear resultado */
			r_temp = (Results *) calloc(1, sizeof(Results));
			if (!r_temp)
				return NULL ;

			r_temp->from = &(gr->heads[i]);
			r_temp->to = n_ptr;

			/* Si no es el primer resultado */
			if (res)
			{
				/* Recorrer los resultados ya guardados */
				for (r_ptr=res; r_ptr->next; r_ptr=r_ptr->next)
				{
					/* Comparar el resultado actual con la confianza del nodo actual */
					/* Si la confianza del nodo actual es mayor, guardar antes del resultado actual */
					if (n_ptr->conf > r_ptr->to->conf)
					{
						r_temp->next = r_ptr;
						r_temp->prev = r_ptr->prev;
						/* Caso inserción como primer elemento de la lista de resultados */
						if (r_ptr != res)
							r_ptr->prev->next = r_temp;
						else
							res = r_temp;
						r_ptr->prev = r_temp;
						break;
					}

				}
				/* Si la confianza del nodo actual es menor o igual, guardar delante del resultado actual */
				/* Cargar al final de la lista de resultados */
				if (!r_temp->next && !r_temp->prev)
				{
					r_ptr->next = r_temp;
					r_temp->prev = r_ptr;
				}
			}
			/* Si es el primer resultado */
			else
			{
				res = r_temp;
			}

			r_temp = NULL;

		}
	}

	return res;
}

/*
 * @requires window_len > 0
 */
Results *
directed_graph(Graph_Adj_List * gr, Sessions * sess, unsigned window_len)
{

	assert(gr);
	assert(sess);
	assert(window_len > 0);

	Window * win = NULL;
	Session * s_ptr = NULL;
	Results * res = NULL;
	unsigned h_index;
	int i, j;

	/* Crear ventana */
	win = window_create(window_len);

	for (i=0 ; i < sess->len; i++)
	{
		/* Vaciar los valores de la ventana */
		window_zero(win);

		s_ptr = &(sess->s[i]);

		for (j=0 ; j < sess->s[i].len ; j++)
		{
			/* Obtener indice del headnode de la URL analizada */
			h_index = adj_list_search_headnode(gr, s_ptr->access[j].url);

			/* Incrementar arcos de la ventana a la URL actual */
			update_arcs_from_window(gr, win, gr->heads[h_index].url);

			/* Actualizar cantidad de apariciones del headnode correspondiente a
			 * la URL analizada
			 */
			update_headnode_appareances(gr, h_index);

			/* Remover objeto más antiguo de la ventana */
			window_purge(win);

			window_add_to_window(win, h_index, gr->heads_len);
		}

	}

	/* Calcular confianza de las transiciones */
	res = calculate_conf2(gr);

	return res;
}



void
window_print(Graph_Adj_List * gr, Window * win)
{
	assert(gr);
	assert(win);

	unsigned i;

	for(i=0 ; i < win->empty ; i++)
		printf("\n\t\t[%u]: %.*s", i, gr->heads[win->window[i]].url->len,
				gr->heads[win->window[i]].url->text);

}

void
results_print(Graph_Adj_List * gr, Results * res)
{
	assert(gr);
	assert(res);

	Results * r_ptr = NULL;
	unsigned i = 1;

	printf("\n\n===== RESULTADOS =====");

	for (r_ptr=res ; r_ptr ; r_ptr=r_ptr->next)
	{
		printf("\n%u Conf: %f %.*s => %.*s", i++, r_ptr->to->conf, r_ptr->from->url->len,
				r_ptr->from->url->text, gr->heads[r_ptr->to->head].url->len, gr->heads[r_ptr->to->head].url->text);

	}

}
